"""
校园新生指引程序 - 数据模型
定义点位、道路、功能等核心数据结构
"""

from typing import List, Dict, Optional
import json


class Point:
    """点位类，代表校园内的具体地点"""
    
    def __init__(self, id: str, name: str, x: float, y: float, functions: List[str], description: str = ""):
        self.id = id
        self.name = name
        self.x = x
        self.y = y
        self.functions = functions
        self.description = description
    
    def __repr__(self):
        return f"Point(id='{self.id}', name='{self.name}', x={self.x}, y={self.y}, functions={self.functions})"
    
    def to_dict(self) -> Dict:
        """转换为字典格式"""
        return {
            "id": self.id,
            "name": self.name,
            "x": self.x,
            "y": self.y,
            "functions": self.functions,
            "description": self.description
        }
    
    @classmethod
    def from_dict(cls, data: Dict) -> 'Point':
        """从字典创建点位对象"""
        return cls(
            id=data["id"],
            name=data["name"],
            x=data["x"],
            y=data["y"],
            functions=data.get("functions", []),
            description=data.get("description", "")
        )


class Road:
    """道路类，代表点位之间的连接关系"""
    
    def __init__(self, id: str, point1_id: str, point2_id: str, distance: float, description: str = ""):
        self.id = id
        self.point1_id = point1_id
        self.point2_id = point2_id
        self.distance = distance
        self.description = description
    
    def __repr__(self):
        return f"Road(id='{self.id}', point1_id='{self.point1_id}', point2_id='{self.point2_id}', distance={self.distance})"
    
    def to_dict(self) -> Dict:
        """转换为字典格式"""
        return {
            "id": self.id,
            "point1_id": self.point1_id,
            "point2_id": self.point2_id,
            "distance": self.distance,
            "description": self.description
        }
    
    @classmethod
    def from_dict(cls, data: Dict) -> 'Road':
        """从字典创建道路对象"""
        return cls(
            id=data["id"],
            point1_id=data["point1_id"],
            point2_id=data["point2_id"],
            distance=data["distance"],
            description=data.get("description", "")
        )


class CampusMap:
    """校园地图类，管理所有点位和道路"""
    
    def __init__(self):
        self.points: Dict[str, Point] = {}  # 点位字典，key为点位ID
        self.roads: Dict[str, Road] = {}  # 道路字典，key为道路ID
        self.function_index: Dict[str, List[str]] = {}  # 功能索引，key为功能名，value为点位ID列表
        self.tag_index: Dict[str, List[str]] = {}  # 标签索引，key为标签名，value为点位ID列表
        self.available_tags: Set[str] = set()  # 可用标签集合
    
    def validate_point_data(self, point_data: Dict) -> bool:
        """验证点位数据有效性"""
        required_fields = ['id', 'name', 'x', 'y', 'functions']
        for field in required_fields:
            if field not in point_data:
                raise ValueError(f"点位数据缺少必要字段: {field}")
        
        if not isinstance(point_data['id'], str) or not point_data['id']:
            raise ValueError("点位ID必须是非空字符串")
        
        if not isinstance(point_data['name'], str) or not point_data['name']:
            raise ValueError("点位名称必须是非空字符串")
        
        try:
            x = float(point_data['x'])
            y = float(point_data['y'])
            if x < 0 or y < 0:
                raise ValueError("点位坐标不能为负数")
        except (ValueError, TypeError):
            raise ValueError("点位坐标必须是有效的数字")
        
        if not isinstance(point_data['functions'], list):
            raise ValueError("功能列表必须是列表类型")
        
        return True
    
    def validate_road_data(self, road_data: Dict) -> bool:
        """验证道路数据有效性"""
        required_fields = ['id', 'point1_id', 'point2_id', 'distance']
        for field in required_fields:
            if field not in road_data:
                raise ValueError(f"道路数据缺少必要字段: {field}")
        
        if not isinstance(road_data['id'], str) or not road_data['id']:
            raise ValueError("道路ID必须是非空字符串")
        
        if not isinstance(road_data['point1_id'], str) or not road_data['point1_id']:
            raise ValueError("起点点位ID必须是非空字符串")
        
        if not isinstance(road_data['point2_id'], str) or not road_data['point2_id']:
            raise ValueError("终点点位ID必须是非空字符串")
        
        if road_data['point1_id'] == road_data['point2_id']:
            raise ValueError("道路的起点和终点不能相同")
        
        try:
            distance = float(road_data['distance'])
            if distance <= 0:
                raise ValueError("道路距离必须大于0")
        except (ValueError, TypeError):
            raise ValueError("道路距离必须是有效的正数")
        
        return True
    
    def add_point(self, point: Point) -> None:
        """添加点位"""
        self.points[point.id] = point
        # 更新功能索引
        for function in point.functions:
            if function not in self.function_index:
                self.function_index[function] = []
            self.function_index[function].append(point.id)
    
    def remove_point(self, point_id: str) -> None:
        """删除点位"""
        if point_id in self.points:
            point = self.points[point_id]
            # 从功能索引中移除
            for function in point.functions:
                if function in self.function_index and point_id in self.function_index[function]:
                    self.function_index[function].remove(point_id)
                    if not self.function_index[function]:
                        del self.function_index[function]
            # 删除点位
            del self.points[point_id]
            # 删除相关的道路
            roads_to_remove = []
            for road_id, road in self.roads.items():
                if road.point1_id == point_id or road.point2_id == point_id:
                    roads_to_remove.append(road_id)
            for road_id in roads_to_remove:
                del self.roads[road_id]
    
    def add_road(self, road: Road) -> None:
        """添加道路"""
        # 检查点位是否存在
        if road.point1_id not in self.points or road.point2_id not in self.points:
            raise ValueError("道路连接的点位不存在")
        self.roads[road.id] = road
    
    def remove_road(self, road_id: str) -> None:
        """删除道路"""
        if road_id in self.roads:
            del self.roads[road_id]
    
    def get_points_by_function(self, function: str) -> List[Point]:
        """根据功能获取点位列表"""
        if function not in self.function_index:
            return []
        return [self.points[point_id] for point_id in self.function_index[function]]
    
    def get_all_functions(self) -> List[str]:
        """获取所有功能列表"""
        return list(self.function_index.keys())
    
    def get_point_by_name(self, name: str) -> Optional[Point]:
        """根据名称获取点位"""
        for point in self.points.values():
            if point.name == name:
                return point
        return None
    
    # 标签管理功能
    def add_tag_to_point(self, point_id: str, tag: str) -> None:
        """为点位添加标签"""
        if point_id not in self.points:
            raise ValueError(f"点位ID不存在: {point_id}")
        
        if tag not in self.tag_index:
            self.tag_index[tag] = []
        
        if point_id not in self.tag_index[tag]:
            self.tag_index[tag].append(point_id)
        
        self.available_tags.add(tag)
    
    def remove_tag_from_point(self, point_id: str, tag: str) -> None:
        """从点位移除标签"""
        if tag in self.tag_index and point_id in self.tag_index[tag]:
            self.tag_index[tag].remove(point_id)
            if not self.tag_index[tag]:
                del self.tag_index[tag]
    
    def get_points_by_tag(self, tag: str) -> List[Point]:
        """根据标签获取点位列表"""
        if tag not in self.tag_index:
            return []
        return [self.points[point_id] for point_id in self.tag_index[tag]]
    
    def get_all_tags(self) -> List[str]:
        """获取所有标签列表"""
        return list(self.available_tags)
    
    def get_tags_for_point(self, point_id: str) -> List[str]:
        """获取点位所有标签"""
        tags = []
        for tag, point_ids in self.tag_index.items():
            if point_id in point_ids:
                tags.append(tag)
        return tags
    
    def export_to_excel(self, filename: str) -> None:
        """导出点位数据到Excel文件"""
        try:
            import pandas as pd
            
            # 准备数据
            data = []
            for point in self.points.values():
                data.append({
                    '点位ID': point.id,
                    '点位名称': point.name,
                    'X坐标': point.x,
                    'Y坐标': point.y,
                    '功能列表': ', '.join(point.functions),
                    '点位描述': point.description,
                    '标签': ', '.join(self.get_tags_for_point(point.id))
                })
            
            # 创建DataFrame并导出
            df = pd.DataFrame(data)
            df.to_excel(filename, index=False, engine='openpyxl')
            
        except ImportError:
            raise ImportError("请安装pandas和openpyxl包以支持Excel导出")
    
    def import_from_excel(self, filename: str) -> None:
        """从Excel文件导入点位数据"""
        try:
            import pandas as pd
            
            # 读取Excel文件
            df = pd.read_excel(filename, engine='openpyxl')
            
            # 清空当前数据
            self.points.clear()
            self.roads.clear()
            self.function_index.clear()
            self.tag_index.clear()
            self.available_tags.clear()
            
            # 导入点位数据
            for _, row in df.iterrows():
                # 处理可能为NaN的值
                functions_str = str(row['功能列表']) if pd.notna(row['功能列表']) else ""
                description_str = str(row.get('点位描述', '')) if pd.notna(row.get('点位描述', '')) else ""
                
                point = Point(
                    id=str(row['点位ID']),
                    name=str(row['点位名称']),
                    x=float(row['X坐标']),
                    y=float(row['Y坐标']),
                    functions=[f.strip() for f in functions_str.split(',') if f.strip()],
                    description=description_str
                )
                self.add_point(point)
                
                # 导入标签
                if '标签' in row and pd.notna(row['标签']):
                    tags_str = str(row['标签'])
                    tags = [t.strip() for t in tags_str.split(',') if t.strip()]
                    for tag in tags:
                        self.add_tag_to_point(point.id, tag)
                        
        except ImportError:
            raise ImportError("请安装pandas和openpyxl包以支持Excel导入")
        except Exception as e:
            raise ValueError(f"Excel文件格式错误: {str(e)}")
    
    def to_dict(self) -> Dict:
        """转换为字典格式"""
        return {
            "points": {pid: point.to_dict() for pid, point in self.points.items()},
            "roads": {rid: road.to_dict() for rid, road in self.roads.items()}
        }
    
    def save_to_file(self, filename: str) -> None:
        """保存到文件"""
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(self.to_dict(), f, ensure_ascii=False, indent=2)
    
    def load_from_file(self, filename: str) -> None:
        """从文件加载"""
        with open(filename, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        # 清空当前数据
        self.points.clear()
        self.roads.clear()
        self.function_index.clear()
        
        # 加载点位
        for point_data in data["points"].values():
            point = Point.from_dict(point_data)
            self.add_point(point)
        
        # 加载道路
        for road_data in data["roads"].values():
            road = Road.from_dict(road_data)
            self.add_road(road)
